<template>
  <div class="map-container">
    <div id="stream-map" ref="mapRef"></div>
    <div v-if="loading" class="loading">正在加载路径数据 road.json ...</div>
    <div v-if="error" class="error">路径数据加载失败，请检查 public/road.json 是否存在且格式正确！</div>
    <div v-if="noData" class="nodata">未检测到有效路径数据！</div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import Map from 'ol/Map'
import View from 'ol/View'
import TileLayer from 'ol/layer/Tile'
import XYZ from 'ol/source/XYZ'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import Feature from 'ol/Feature'
import LineString from 'ol/geom/LineString'
import { Style, Stroke } from 'ol/style'
import { fromLonLat } from 'ol/proj'

const mapRef = ref<HTMLElement | null>(null)
let map: Map | null = null
let animationFrameId: number | null = null
let streamFeature: Feature | null = null
let pathCoords: number[][] = []

const vectorSource = new VectorSource()
const vectorLayer = new VectorLayer({ source: vectorSource })

// 状态
const loading = ref(true)
const error = ref(false)
const noData = ref(false)

// 流线动画参数
const streamSpeed = 0.0001 // 控制流线移动速度
const streamLength = 0.5   // 流线段占总长的比例

// 流线样式
const streamStyle = new Style({
  stroke: new Stroke({
    color: 'rgba(0, 234, 255, 0.7)',
    width: 8,
    lineCap: 'round',
    lineJoin: 'round'
  })
})
const glowStyle = new Style({
  stroke: new Stroke({
    color: 'rgba(0, 234, 255, 0.2)',
    width: 24,
    lineCap: 'round',
    lineJoin: 'round'
  })
})

function updateStreamAnimation() {
  if (!streamFeature || pathCoords.length < 2) return
  const line = new LineString(pathCoords.map(c => fromLonLat(c)))
  const totalLength = line.getLength()
  const now = Date.now()
  const t = ((now * streamSpeed) % 1)
  const startFrac = t
  const endFrac = Math.min(t + streamLength, 1)
  const startLen = totalLength * startFrac
  const endLen = totalLength * endFrac
  const coords: number[][] = []
  let accLen = 0
  const allCoords = line.getCoordinates()
  for (let i = 0; i < allCoords.length - 1; i++) {
    const segStart = allCoords[i]
    const segEnd = allCoords[i + 1]
    const segLen = new LineString([segStart, segEnd]).getLength()
    if (accLen + segLen < startLen) {
      accLen += segLen
      continue
    }
    if (coords.length === 0) {
      const ratio = (startLen - accLen) / segLen
      coords.push([
        segStart[0] + (segEnd[0] - segStart[0]) * ratio,
        segStart[1] + (segEnd[1] - segStart[1]) * ratio
      ])
    }
    if (accLen + segLen > endLen) {
      const ratio = (endLen - accLen) / segLen
      coords.push([
        segStart[0] + (segEnd[0] - segStart[0]) * ratio,
        segStart[1] + (segEnd[1] - segStart[1]) * ratio
      ])
      break
    } else {
      coords.push(segEnd)
    }
    accLen += segLen
  }
  if (coords.length >= 2) {
    streamFeature.setGeometry(new LineString(coords))
  }
  animationFrameId = requestAnimationFrame(updateStreamAnimation)
}

async function loadRoadJson() {
  loading.value = true
  error.value = false
  noData.value = false
  try {
    const res = await fetch('/road.json')
    const geojson = await res.json()
    if (geojson.features && geojson.features.length > 0) {
      pathCoords = geojson.features[0].geometry.coordinates
      // 居中并缩放到路径范围
      if (map) {
        const allCoords = pathCoords.map(c => fromLonLat(c))
        let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity
        allCoords.forEach((coord: number[]) => {
          const [x, y] = coord
          minX = Math.min(minX, x)
          minY = Math.min(minY, y)
          maxX = Math.max(maxX, x)
          maxY = Math.max(maxY, y)
        })
        map.getView().fit([minX, minY, maxX, maxY], {
          padding: [40, 40, 40, 40],
          duration: 800
        })
      }
      vectorSource.clear()
      streamFeature = new Feature()
      streamFeature.setStyle([glowStyle, streamStyle])
      vectorSource.addFeature(streamFeature)
      animationFrameId = requestAnimationFrame(updateStreamAnimation)
    } else {
      noData.value = true
    }
    loading.value = false
  } catch (e) {
    error.value = true
    loading.value = false
    console.error('加载road.json失败', e)
  }
}

onMounted(() => {
  if (!mapRef.value) return
  map = new Map({
    target: mapRef.value,
    layers: [
      new TileLayer({
        source: new XYZ({
          url: 'https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png'
        })
      }),
      vectorLayer
    ],
    view: new View({
      center: fromLonLat([114.1, 24.2]),
      zoom: 8
    })
  })
  loadRoadJson()
})

onUnmounted(() => {
  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId)
  }
  if (map) {
    map.setTarget(undefined)
  }
})
</script>

<style scoped>
.map-container {
  width: 100vw;
  height: 100vh;
  background: #111;
  position: relative;
}
#stream-map {
  width: 100%;
  height: 100%;
}
.loading, .error, .nodata {
  position: absolute;
  left: 50%;
  top: 30px;
  transform: translateX(-50%);
  background: rgba(0,0,0,0.7);
  color: #00eaff;
  padding: 8px 24px;
  border-radius: 8px;
  font-size: 18px;
  z-index: 10;
}
.error { color: #ff4d4f; }
.nodata { color: #ffd700; }
</style>

<script lang="ts">
export default {
  name: 'StreamLineMap'
}
</script> 